package com.jy.modules.cims.common.sysUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.PropertyUtils;

/**
 * @description 扩展<code>org.apache.commons.beanutils.BeanUtils</code>,
 *              提供一些反射方面缺失功能的封装。
 * @version v1.0
 * @author ShengZhoutao
 * @Date 2012-9-27
 */
public final class SimpleBeanUtils extends org.apache.commons.beanutils.BeanUtils {
	/**
	 * 直接读取对象属性值, 忽略private/protected修饰符, 不经过getter函数.
	 * 
	 * @param object
	 *            目标对象
	 * @param fieldName
	 *            属性名称
	 * @return Object 属性值
	 * @throws NoSuchFieldException
	 */
	public static Object getFieldValue(final Object object, final String fieldName) throws NoSuchFieldException {
		Field field = getDeclaredField(object.getClass(), fieldName);
		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}
		makeAccessible(field);
		Object result = null;
		try {
			result = field.get(object);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 直接读取对象属性值, 忽略private/protected修饰符, 不经过getter函数.
	 * 
	 * @param object
	 *            目标对象
	 * @param fieldName
	 *            属性名称
	 * @param value
	 *            属性值
	 * @throws NoSuchFieldException
	 */
	public static void setFieldValue(Object object, final String fieldName, final Object value) throws NoSuchFieldException {
		Field field = getDeclaredField(object.getClass(), fieldName);
		if (field == null) {
			throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + object + "]");
		}
		makeAccessible(field);
		try {
			field.set(object, value);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 循环向上转型,获取对象的DeclaredField.
	 * 
	 * @param clazz
	 *            目标类
	 * @param propertyName
	 *            属性名称
	 * @return Field 字段对象
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Class<?> clazz, String propertyName) throws NoSuchFieldException {
		for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredField(propertyName);
			} catch (NoSuchFieldException e) {
				// Field不在当前类定义,继续向上转型
			}
		}
		throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
	}

	/**
	 * 循环向上转型, 获取对象的DeclaredMethod.
	 * 
	 * @param clazz
	 *            目标类
	 * @param methodName
	 *            方法名称
	 * @param parameterTypes
	 *            参数类型数组
	 * @return Method 方法对象
	 * @throws NoSuchMethodException
	 */
	public static Method getDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes) throws NoSuchMethodException {
		for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
			try {
				return superClass.getDeclaredMethod(methodName, parameterTypes);
			} catch (NoSuchMethodException e) {// NOSONAR
				// Method不在当前类定义,继续向上转型
				e.printStackTrace();
			}
		}
		throw new NoSuchMethodException("No such mothod: " + clazz.getName() + '.' + methodName);
	}

	/**
	 * 直接调用对象方法, 忽略private/protected修饰符.
	 * 
	 * @param object
	 *            目标对象
	 * @param methodName
	 *            方法名称
	 * @param parameters
	 *            方法参数
	 * @return Object 方法执行结果
	 * @throws NoSuchMethodException
	 */
	public static Object invokeMethod(final Object object, final String methodName, final Object... parameters) throws NoSuchMethodException {
		Class<?>[] types = new Class[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			types[i] = parameters[i].getClass();
		}
		Method method = getDeclaredMethod(object.getClass(), methodName, types);
		if (method == null) {
			throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + object + "]");
		}
		method.setAccessible(true);
		try {
			return method.invoke(object, parameters);
		} catch (Exception e) {
			throw convertReflectionExceptionToUnchecked(e);
		}
	}

	/**
	 * 将反射时的checked exception转换为unchecked exception.
	 * 
	 * @param e
	 *            检查异常
	 * @return RuntimeException 运行期异常
	 */
	public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
		if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException || e instanceof NoSuchMethodException) {
			return new IllegalArgumentException("Reflection Exception.", e);
		} else if (e instanceof InvocationTargetException) {
			return new RuntimeException("Reflection Exception.", ((InvocationTargetException) e).getTargetException());
		} else if (e instanceof RuntimeException) {
			return (RuntimeException) e;
		}
		return new RuntimeException("Unexpected Checked Exception.", e);
	}

	/**
	 * 对象属性复制
	 * 
	 * @param dest
	 *            目标对象
	 * @param orig
	 *            源对象
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
		ConvertUtils.register(new DateConvert(), java.util.Date.class);
		ConvertUtils.register(new DateConvert(), java.sql.Date.class);
		org.apache.commons.beanutils.BeanUtils.copyProperties(dest, orig);
	}

	/**
	 * 强行设置Field可访问.
	 * 
	 * @param field
	 *            目标字段
	 */
	private static void makeAccessible(final Field field) {
		if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
			field.setAccessible(true);
		}
	}

	/**
	 * @description 覆盖 apache.commons.beanutils 的日期转换器。
	 * 
	 * @version v1.0
	 * @author ShengZhoutao
	 * @Date 2012-9-27
	 */
	private static class DateConvert implements org.apache.commons.beanutils.Converter {

		private SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

		private SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		private SimpleDateFormat df3 = new SimpleDateFormat("yyyy-MM-dd");

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.apache.commons.beanutils.Converter#convert(java.lang.Class,
		 * java.lang.Object)
		 */
		@SuppressWarnings("rawtypes")
		public Object convert(Class clazz, Object object) {
			if (object instanceof Date) {
				return object;
			} else if (object instanceof String) {
				String value = (String) object;
				if (value == null || value.trim().length() == 0) {
					return null;
				}
				try {
					return df1.parse(value.trim());
				} catch (Exception e) {
					try {
						return df2.parse(value.trim());
					} catch (ParseException ex) {
						try {
							return df3.parse(value.trim());
						} catch (ParseException exx) {
							return null;
						}
					}
				}
			}
			return object;
		}
	}

	/**
	 * 对象拷贝 数据对象空值不拷贝到目标对象
	 * 
	 * @param dataObject
	 *            数据对象
	 * @param tobean
	 *            目标对象
	 * @throws NoSuchMethodException
	 * 
	 */
	public static void copyBeanNotNull2Bean(Object databean, Object tobean) throws Exception {
		PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(databean);
		for (int i = 0; i < origDescriptors.length; i++) {
			String name = origDescriptors[i].getName();
			// String type = origDescriptors[i].getPropertyType().toString();
			if ("class".equals(name)) {
				continue; // No point in trying to set an object's class
			}
			if (PropertyUtils.isReadable(databean, name) && PropertyUtils.isWriteable(tobean, name)) {
				try {
					Object value = PropertyUtils.getSimpleProperty(databean, name);
					if (value != null) {
						copyProperty(tobean, name, value);
					}
				} catch (java.lang.IllegalArgumentException ie) {
					; // Should not happen
				} catch (Exception e) {
					; // Should not happen
				}

			}
		}
	}

	private static void convert(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {

		// Validate existence of the specified beans
		if (dest == null) {
			throw new IllegalArgumentException("No destination bean specified");
		}
		if (orig == null) {
			throw new IllegalArgumentException("No origin bean specified");
		}

		// Copy the properties, converting as necessary
		if (orig instanceof DynaBean) {
			DynaProperty origDescriptors[] = ((DynaBean) orig).getDynaClass().getDynaProperties();
			for (int i = 0; i < origDescriptors.length; i++) {
				String name = origDescriptors[i].getName();
				if (PropertyUtils.isWriteable(dest, name)) {
					Object value = ((DynaBean) orig).get(name);
					try {
						copyProperty(dest, name, value);
					} catch (Exception e) {
						; // Should not happen
					}

				}
			}
		} else if (orig instanceof Map) {
			Iterator<?> names = ((Map<?, ?>) orig).keySet().iterator();
			while (names.hasNext()) {
				String name = (String) names.next();
				if (PropertyUtils.isWriteable(dest, name)) {
					Object value = ((Map<?, ?>) orig).get(name);
					try {
						copyProperty(dest, name, value);
					} catch (Exception e) {
						; // Should not happen
					}
				}
			}
		} else
		/* if (orig is a standard JavaBean) */
		{
			PropertyDescriptor origDescriptors[] = PropertyUtils.getPropertyDescriptors(orig);
			for (int i = 0; i < origDescriptors.length; i++) {
				String name = origDescriptors[i].getName();
				// String type =
				// origDescriptors[i].getPropertyType().toString();
				if ("class".equals(name)) {
					continue; // No point in trying to set an object's class
				}
				if (PropertyUtils.isReadable(orig, name) && PropertyUtils.isWriteable(dest, name)) {
					try {
						Object value = PropertyUtils.getSimpleProperty(orig, name);
						copyProperty(dest, name, value);
					} catch (java.lang.IllegalArgumentException ie) {
						; // Should not happen
					} catch (Exception e) {
						; // Should not happen
					}
				}
			}
		}
	}

	/**
	 * 把orig和dest相同属性的value复制到dest中
	 * 
	 * @param dest
	 *            目标对象
	 * @param orig
	 *            源对象
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static void copyBean2Bean(Object dest, Object orig) throws Exception {
		convert(dest, orig);
	}

	public static void copyBean2Map(Map<String, Object> map, Object bean) {
		PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(bean);
		for (int i = 0; i < pds.length; i++) {
			PropertyDescriptor pd = pds[i];
			String propname = pd.getName();
			try {
				Object propvalue = PropertyUtils.getSimpleProperty(bean, propname);
				map.put(propname, propvalue);
			} catch (IllegalAccessException e) {
				// e.printStackTrace();
			} catch (InvocationTargetException e) {
				// e.printStackTrace();
			} catch (NoSuchMethodException e) {
				// e.printStackTrace();
			}
		}
	}

	/**
	 * 将Map内的key与Bean中属性相同的内容复制到BEAN中
	 * 
	 * @param bean
	 *            Object
	 * @param properties
	 *            Map
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static void copyMap2Bean(Object bean, Map<?, ?> properties) throws IllegalAccessException, InvocationTargetException {

		if ((bean == null) || (properties == null)) {
			return;
		}

		Iterator<?> names = properties.keySet().iterator();
		while (names.hasNext()) {
			String name = (String) names.next();
			if (name == null) {
				continue;
			}

			Object value = properties.get(name);
			try {
				Class<?> clazz = PropertyUtils.getPropertyType(bean, name);
				if (null == clazz) {
					continue;
				}
				String className = clazz.getName();
				if (className.equalsIgnoreCase("java.sql.Timestamp")) {
					if (value == null || value.equals("")) {
						continue;
					}
				}
				setProperty(bean, name, value);
			} catch (NoSuchMethodException e) {
				continue;
			}
		}
	}

	/**
	 * Map内的key与Bean中属性相同的内容复制到BEAN中 对于存在空值的取默认值
	 * 
	 * @param bean
	 *            Object
	 * @param properties
	 *            Map
	 * @param defaultValue
	 *            String
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static void copyMap2Bean(Object bean, Map<?, ?> properties, String defaultValue) throws IllegalAccessException, InvocationTargetException {
		// Do nothing unless both arguments have been specified
		if ((bean == null) || (properties == null)) {
			return;
		}
		// Loop through the property name/value pairs to be set
		Iterator<?> names = properties.keySet().iterator();
		while (names.hasNext()) {
			String name = (String) names.next();
			// Identify the property name and value(s) to be assigned
			if (name == null) {
				continue;
			}
			Object value = properties.get(name);
			try {
				Class<?> clazz = PropertyUtils.getPropertyType(bean, name);
				if (null == clazz) {
					continue;
				}
				String className = clazz.getName();
				if (className.equalsIgnoreCase("java.sql.Timestamp")) {
					if (value == null || value.equals("")) {
						continue;
					}
				}
				if (className.equalsIgnoreCase("java.lang.String")) {
					if (value == null) {
						value = defaultValue;
					}
				}
				setProperty(bean, name, value);
			} catch (NoSuchMethodException e) {
				continue;
			}
		}
	}
}
